<template>
	<view class="container">
		<v-tabs v-model="currentTab" :tabs="tabs" :scroll="false" bgColor="#e8f4eb" @change="onTabChange"></v-tabs>
		<!-- 总分栏 -->
		<view v-if="totalRecordables > 0" class="score-bar">
			<!-- <text class="score-text">{{ totalScored }}/{{ totalMaxScore }}</text> -->
			<text class="score-sub">（已评 {{ evaluatedCount }} / 可录 {{ totalRecordables }}）</text>
		</view>

		<!-- 绘本区域 :open="bIndex == 0"-->
		<view v-if="currentTab === 0 && pictureBooks.length" class="section">
			<uni-collapse ref="collapse" class="collapse-box">
				<uni-collapse-item v-for="(book, bIndex) in pictureBooks" :key="book.id" >
					<!-- 自定义标题：书本图标 + 书名 -->
					<template #title>
						<view class="book-title">
							📖&nbsp;<text class="book-name">{{ book.bookName }}</text>
						</view>
					</template>

					<!-- 书页卡片 -->
					<view v-for="(page, pIndex) in book.pages" :key="pIndex" class="ppt-card">
						<text class="ppt-code">第 {{ pIndex + 1 }} 页</text>
						<image :src="imagePrefix + page.pictureBookPage" mode="widthFix" class="ppt-image"
							@click="previewImage(book.pages.map(p => imagePrefix + p.pictureBookPage), pIndex)" />

						<!-- 图片下：文字 + 喇叭播放（后端音频） -->
						<view class="text-row" @click.stop="playAudio(imagePrefix + page.vocabularyAudio)">
							<image v-if="page.vocabularyAudio" src="../../static/icon/voice.png" class="speaker"
								:class="{ playing: playingSrc === imagePrefix + page.vocabularyAudio }" />
							<text class="meaning-text">{{ page.pageText || ''}} </text>
						</view>

						<!-- 图片下：本地录音（每页可录制） -->
						<view v-if="page.vocabularyAudio" class="record-row">
							<!-- 未录音：只显示 录音 按钮 -->
							<template v-if="!page._recordPath">
								<button class="record-btn" size="mini"
									:class="{ recording: recording && recordingKey === recKey(bIndex,pIndex) }"
									@click.stop="toggleRecord(bIndex, pIndex)">
									<image src="../../static/icon/mic.png" class="mic-icon" mode="widthFix" />
									<text class="btn-text">
										{{ recording && recordingKey === recKey(bIndex,pIndex) ? '正在录音… 点击停止' : '练习打卡' }}
									</text>
								</button>
							</template>

							<!-- 已录音：显示 播放 + 删除 -->
							<template v-else>
								<button class="play-rec-btn" size="mini" @click.stop="playAudio(page._recordPath)">
									<image src="../../static/icon/voice.png" class="play-icon" mode="widthFix" />
									<text class="btn-text">播放录音</text>
									<text class="dur"
										v-if="page._recordDuration">（{{ formatDuration(page._recordDuration) }}）</text>
								</button>

								<button class="del-rec-btn danger" size="mini"
									@click.stop="confirmDeleteRecord('p', bIndex, pIndex)">
									删除录音
								</button>
							</template>
						</view>

						<!-- AI 反馈（页） -->
						<view v-if="page._ai_message" class="ai-feedback">
							🤖
							<text>{{ page._ai_message }}</text><!-- （评分：{{ page._ai_score }}）({{ page._original_score }}) -->
						</view>
					</view>
				</uni-collapse-item>
			</uni-collapse>
		</view>

		<!-- 单词区域 -->
		<!-- <view v-if="currentTab === 1 && vocabularyList.length" class="section">
			<view v-for="(word, wIndex) in vocabularyList" :key="word.id" class="ppt-card">
				<text class="ppt-code">单词 {{ wIndex + 1 }}</text>
				<image :src="imagePrefix + word.image" mode="widthFix" class="ppt-image"
					@click="previewImage(vocabularyList.map(w => imagePrefix + w.image), wIndex)" />
				
				<view class="text-row" @click.stop="playAudio(imagePrefix + word.audio)">
					<image v-if="word.audio" src="../../static/icon/voice.png" class="speaker"
						:class="{ playing: playingSrc === imagePrefix + word.audio }" />
					<text class="meaning-text">{{ word.text }}</text>
				</view>

				<view class="record-row">
					<template v-if="!word._recordPath">
						<button class="record-btn" size="mini"
							:class="{ recording: recording && recordingKey === recKey('v',wIndex) }"
							@click.stop="toggleRecord('v', wIndex)">
							<image src="../../static/icon/mic.png" class="mic-icon" mode="widthFix" />
							<text class="btn-text">
								{{ recording && recordingKey === recKey('v',wIndex) ? '正在录音… 点击停止' : '录音' }}
							</text>
						</button>
					</template>

					<template v-else>
						<button class="play-rec-btn" size="mini" @click.stop="playAudio(word._recordPath)">
							<image src="../../static/icon/voice.png" class="play-icon" mode="widthFix" />
							<text class="btn-text">播放录音</text>
							<text class="dur"
								v-if="word._recordDuration">（{{ formatDuration(word._recordDuration) }}）</text>
						</button>

						<button class="del-rec-btn danger" size="mini" @click.stop="confirmDeleteRecord('v', wIndex)">
							删除录音
						</button>
					</template>
				</view>

				<view v-if="word._ai_message" class="ai-feedback">
					🤖 <text>{{ word._ai_message }}</text>（评分：{{ word._ai_score }}）
				</view>
			</view>
		</view> -->
		<view class="submit-score-btn" @click="submitScore">
			🏆 提交积分（{{ totalScored }}/{{ totalMaxScore }}）
		</view>
	</view>
</template>

<script>
	import api from '@/api/api.js';

	export default {
		data() {
			return {
				classId: '',
				pictureBookId: '',
				vocabularyId: '',
				pictureBooks: [],
				vocabularyList: [],
				imagePrefix: uni.getStorageSync('requestPath') || '',
				tabs: ['绘本'], //, '单词'
				currentTab: 0,
				innerAudioContext: null,
				playingSrc: '',
				recorderManager: null,
				recording: false,
				recordingKey: '',
				recordStartTs: 0,
				uploading: false,
				studentId: '',
			};
		},
		onLoad(options) {
			this.classId = options.classId || '';
			this.pictureBookId = options.pictureBookId || '';
			this.vocabularyId = options.vocabularyId || '';
			this.studentId = options.studentId || '';
			this.createAudio();
			this.createRecorder();
			this.loadData();

		},
		onUnload() {
			if (this.innerAudioContext) {
				this.innerAudioContext.destroy();
				this.innerAudioContext = null;
			}
			if (this.recorderManager) {
				try {
					this.recorderManager.stop && this.recorderManager.stop();
				} catch (e) {}
				this.recorderManager = null;
			}
		},
		beforeDestroy() {
			if (this.innerAudioContext) {
				this.innerAudioContext.destroy();
				this.innerAudioContext = null;
			}
			if (this.recorderManager) {
				try {
					this.recorderManager.stop && this.recorderManager.stop();
				} catch (e) {}
				this.recorderManager = null;
			}
		},
		computed: {
			// 可录音的总条数（与UI保持一致：绘本页需要有 vocabularyAudio，单词全算）
			totalRecordables() {
				const pageCount = (this.pictureBooks || []).reduce((acc, b) => {
					const pages = b?.pages || [];
					return acc + pages.filter(p => !!p?.vocabularyAudio).length;
				}, 0);
				const wordCount = (this.vocabularyList || []).length;
				return pageCount + wordCount;
			},

			// 已经有AI评分的条数
			evaluatedCount() {
				const pageEval = (this.pictureBooks || []).reduce((acc, b) => {
					const pages = b?.pages || [];
					return acc + pages.filter(p => p && p._ai_score != null && p._ai_score !== '').length;
				}, 0);
				const wordEval = (this.vocabularyList || []).filter(w => w && w._ai_score != null && w._ai_score !== '')
					.length;
				return pageEval + wordEval;
			},

			// 单条录音的得分计算：四舍五入(ai_score / 可录总数)
			_perItemScoreFn() {
				return (aiScore) => {
					const s = Number(aiScore || 0);
					// 每条满分固定10分：四舍五入(s/10)，并夹在[0,10]
					return Math.max(0, Math.min(10, Math.round(s / 10)));
				};
			},

			// 总得分 = 所有已评录音的单条得分之和
			totalScored() {
				const f = this._perItemScoreFn;
				let sum = 0;

				(this.pictureBooks || []).forEach(b => {
					(b?.pages || []).forEach(p => {
						if (p && p._ai_score != null && p._ai_score !== '') {
							sum += f(p._ai_score);
						}
					});
				});
				(this.vocabularyList || []).forEach(w => {
					if (w && w._ai_score != null && w._ai_score !== '') {
						sum += f(w._ai_score);
					}
				});

				// 以 totalMaxScore 为上限
				return Math.min(this.totalMaxScore, sum);
			},


			// 固定100分
			totalMaxScore() {
				return this.totalRecordables * 10;
			},
		},

		methods: {
			_getReferenceTextByKey(key) {
				// key 形如 'p:bookIndex:pageIndex' 或 'v:v:wordIndex'
				const [type, i, j] = (key || '').split(':');
				if (type === 'p') {
					const bi = Number(i),
						pj = Number(j);
					const page = this.pictureBooks?.[bi]?.pages?.[pj];
					return page?.pageText || '';
				} else if (type === 'v') {
					const wi = Number(j); // recKey('v', wIndex) -> v:v:index
					const word = this.vocabularyList?.[wi];
					return word?.text || '';
				}
				return '';
			},

			// 上传打卡：文件字段名 file；随表单传 referenceText 与 fileType=student_review_checkin
			async practicePronounce(filePath, referenceText, key) {
				if (!filePath) return;
				const baseUrl = uni.getStorageSync('requestPath') || '';
				const url = `${baseUrl}/app/common/uploadFilesByType`;
				const token = uni.getStorageSync('token') || '';

				this.uploading = true;
				uni.showLoading({
					title: '智能测评中...'
				});
				try {
					const res = await new Promise((resolve, reject) => {
						uni.uploadFile({
							url,
							filePath,
							name: 'file',
							formData: {
								referenceText: referenceText || '',
								fileType: 'student_review_checkin'
							},
							header: token ? {
								AppToken: `${token}`
							} : {},
							success: resolve,
							fail: reject
						});
					});

					// 解析 JSON 到 data，不和 res 混用
					const data = typeof res.data === 'string' ? JSON.parse(res.data) : (res.data || {});

					// 兼容字段命名与嵌套
					const fileNameField = data.fileName || data.filename || data?.data?.fileName || data?.data
						?.filename || '';


					// 如果后端返回了完整的 ai_response，则解析它
					let score = -1;
					let original_score = null;
					let message = '暂无评价';

					try {
						if (data.ai_response) {
							const ai = typeof data.ai_response === 'string' ? JSON.parse(data.ai_response) : data
								.ai_response;

							// ===== 整体评分部分 =====
							const suggested = ai?.result?.suggestedScore ?? -1;
							const accuracy = ai?.result?.pronAccuracy ?? null;
							const fluency = ai?.result?.pronFluency ?? null;
							const completion = ai?.result?.pronCompletion ?? null;

							score = suggested;
							original_score = accuracy;

							// 评分区间文字说明
							let levelDesc = '';
							if (suggested >= 90) levelDesc = '（整体水平优秀👍）';
							else if (suggested >= 75) levelDesc = '（整体水平良好）';
							else if (suggested >= 60) levelDesc = '（整体水平中等偏低）';
							else levelDesc = '（整体水平较弱，建议多练习）';

							// ===== 单词逐项分析 =====
							const words = ai?.result?.words || [];
							const details = words.map(w => {
								const phones = w.phoneInfos || [];
								const lowPhones = phones.filter(p => p.pronAccuracy < 75);
								const highPhones = phones.filter(p => p.pronAccuracy >= 90);
								let line = '';

								if (lowPhones.length > 0) {
									// 通用提示，不区分音素种类
									const wrongPhs = lowPhones.map(p => p.phone).join('、');
									line = `‼️️ ｜${w.word}｜：${wrongPhs} 发音不够清晰或不标准`;
								} else if (highPhones.length === phones.length && phones.length > 0) {
									line = `✅ ｜${w.word}｜$：发音标准，很棒！`;
								} else {
									line = `🔔 ｜${w.word}｜$：整体可理解，可更自然一点`;
								}

								return line;
							});


							// ===== 综合提示 =====
							const lowCount = words.filter(w => (w.phoneInfos || []).some(p => p.pronAccuracy < 70))
								.length;
							let summaryTip = '';
							if (lowCount === 0) summaryTip = '🌟 发音整体非常好，保持！';
							else if (lowCount <= 2) summaryTip = '👍 大部分发音准确，可稍加强细节练习。';
							else summaryTip = '🎯 多个单词存在发音偏差，建议放慢语速重新读一遍。';

							// ===== 组合最终反馈文本 =====
							message =
								`${summaryTip}\n\n` +
								`🏆 建议评分： ${suggested.toFixed(1)} 分 ${levelDesc}\n\n` +
								`🔍 逐词反馈：\n` +
								details.join('，\n');
						} else {
							message = data.ai_message ?? data.aiMessage ?? '暂无评价';
							score = data.ai_adjust_score ?? data.aiScore ?? -1;
							original_score = data.ai_original_score ?? data.originalScore ?? null;
						}
					} catch (e) {
						console.error('解析 ai_response 失败:', e);
						message = 'AI 评分解析异常';
					}




					// 若已是完整 URL 则直接用；否则用 baseUrl 拼接；都没有则回退本地临时文件
					const recordUrl = /^https?:\/\//i.test(fileNameField) ?
						fileNameField :
						(fileNameField ? (baseUrl + fileNameField) : filePath);

					// 仅当明显乱码时尝试转码
					let finalMessage = message;
					if (typeof finalMessage === 'string' && /Ã|Â|æ|è¯/.test(finalMessage)) {
						try {
							finalMessage = decodeURIComponent(escape(finalMessage));
						} catch (e) {}
					}

					// 使用 onStop 传入的 key 来定位，避免 this.recordingKey 已清空
					const [type, i, j] = (key || '').split(':');
					if (type === 'p') {
						const bi = Number(i),
							pj = Number(j);
						this.$set(this.pictureBooks[bi].pages[pj], '_recordPath', recordUrl);
						this.$set(this.pictureBooks[bi].pages[pj], '_ai_message', finalMessage);
						this.$set(this.pictureBooks[bi].pages[pj], '_ai_score', Number(score));
						this.$set(this.pictureBooks[bi].pages[pj], '_original_score', Number(original_score));
					} else if (type === 'v') {
						const wi = Number(j);
						this.$set(this.vocabularyList[wi], '_recordPath', recordUrl);
						this.$set(this.vocabularyList[wi], '_ai_message', finalMessage);
						this.$set(this.vocabularyList[wi], '_ai_score', Number(score));
						this.$set(this.vocabularyList[wi], '_original_score', Number(original_score));
					}

					uni.showToast({
						title: `AI评分 ${score}分`,
						icon: 'none'
					});
				} catch (err) {
					console.error('practicePronounce 上传失败', err);
					uni.showToast({
						title: '上传失败，请重试',
						icon: 'none'
					});
				} finally {
					this.uploading = false;
					uni.hideLoading();
				}
			},

			previewImage(urls, current) {
				uni.previewImage({
					urls,
					current: urls[current]
				});
			},

			loadData() {
				if (this.pictureBookId) {
					api.getPictureBookByIds({
						ids: this.pictureBookId
					}).then(res1 => {
						if (res1 && res1.data) {
							this.pictureBooks = res1.data.map(item => {
								let pages = [];
								try {
									pages = JSON.parse(item.pageListJson || '[]');
								} catch (e) {
									console.error('解析pageListJson失败', e);
								}
								
								return {
									...item,
									pages
								};
								
							});
							this.$nextTick(() => {
								this.$refs.collapse.resize()
							})
							this.listByStudentAndClass();
						}
					});
				}
				// if (this.vocabularyId) {
				// 	api.getVocabularyByIds({
				// 		ids: this.vocabularyId
				// 	}).then(res2 => {
				// 		if (res2 && res2.data) {
				// 			this.vocabularyList = res2.data.map(item => ({
				// 				id: item.id,
				// 				image: item.vocabImage,
				// 				audio: item.vocabAudio,
				// 				text: item.vocabText
				// 			}));
				// 		}
				// 	});
				// }
			},

			onTabChange(index) {
				this.currentTab = index;
			},

			createAudio() {
				if (!this.innerAudioContext) {
					this.innerAudioContext = uni.createInnerAudioContext();
					this.innerAudioContext.autoplay = false;
					this.innerAudioContext.onStop(() => (this.playingSrc = ''));
					this.innerAudioContext.onEnded(() => (this.playingSrc = ''));
					this.innerAudioContext.onError(() => (this.playingSrc = ''));
				}
			},

			confirmDeleteRecord(type, i, j) {
				uni.showModal({
					title: '确认删除',
					content: '删除后无法恢复，确定要删除这段录音吗？',
					confirmText: '删除',
					cancelText: '取消',
					confirmColor: '#e74c3c',
					success: (res) => {
						if (!res.confirm) return;
						if (type === 'p') {
							if (this.pictureBooks[i] && this.pictureBooks[i].pages[j]) {
								this.$delete(this.pictureBooks[i].pages[j], '_recordPath');
								this.$delete(this.pictureBooks[i].pages[j], '_recordDuration');
								this.$delete(this.pictureBooks[i].pages[j], '_ai_message');
								this.$delete(this.pictureBooks[i].pages[j], '_ai_score');
								this.$delete(this.pictureBooks[i].pages[j], '_original_score');

							}
						} else if (type === 'v') {
							if (this.vocabularyList[i]) {
								this.$delete(this.vocabularyList[i], '_recordPath');
								this.$delete(this.vocabularyList[i], '_recordDuration');
								this.$delete(this.vocabularyList[i], '_ai_message');
								this.$delete(this.vocabularyList[i], '_ai_score');
								this.$delete(this.vocabularyList[i], '_original_score');
							}
						}
						uni.showToast({
							title: '已删除',
							icon: 'none'
						});
						this.$nextTick(() => {
							this.$refs.collapse.resize()
						})
					}
				});
			},

			createRecorder() {
				try {
					this.recorderManager = uni.getRecorderManager();
					this.recorderManager.onStart(() => {
						this.recording = true;
					});
					this.recorderManager.onStop(res => {
						const path = res.tempFilePath;
						const dur = Date.now() - this.recordStartTs;
						const key = this.recordingKey; // 先保存，后面 practicePronounce 要用

						this.recording = false;

						// 先把本地时长/路径落到 UI
						const [type, i, j] = (key || '').split(':');
						if (type === 'p') {
							const bi = Number(i),
								pj = Number(j);
							this.$set(this.pictureBooks[bi].pages[pj], '_recordPath', path);
							this.$set(this.pictureBooks[bi].pages[pj], '_recordDuration', dur);
						} else if (type === 'v') {
							const wi = Number(j);
							this.$set(this.vocabularyList[wi], '_recordPath', path);
							this.$set(this.vocabularyList[wi], '_recordDuration', dur);
						}

						// 上传打卡
						const referenceText = this._getReferenceTextByKey(key);
						this.practicePronounce(path, referenceText, key).finally(() => {
							// 最后再清空，以免异步过程中读取不到 key
							this.recordingKey = '';
							this.$nextTick(() => {
								this.$refs.collapse.resize()
							})
						});

					});
					this.recorderManager.onError(err => {
						this.recording = false;
						this.recordingKey = '';
						uni.showToast({
							title: '录音失败',
							icon: 'none'
						});
						console.error('recorder error', err);
					});
				} catch (e) {
					console.warn('当前平台不支持录音');
				}
			},

			recKey(a, b) {
				// a: 'v' 或 bookIndex；b: pageIndex 或 wordIndex
				return typeof a === 'string' ? `v:${a}:${b}` : `p:${a}:${b}`;
			},

			async toggleRecord(a, b) {
				if (!this.recorderManager) {
					uni.showToast({
						title: '当前平台不支持录音',
						icon: 'none'
					});
					return;
				}
				const key = this.recKey(a, b);
				if (this.recording && this.recordingKey === key) {
					try {
						this.recorderManager.stop();
					} catch (e) {}
					return;
				}
				if (this.recording && this.recordingKey !== key) {
					try {
						this.recorderManager.stop();
					} catch (e) {}
				}
				// 开始新的录音
				this.recordingKey = key;
				this.recordStartTs = Date.now();
				try {
					this.recorderManager.start({
						format: 'mp3', // 若评测要求 PCM16/WAV，可改为 wav，并在后端转码
						numberOfChannels: 1,
						sampleRate: 16000,
						encodeBitRate: 48000,
						duration: 60000 // 最长60s
					});
				} catch (e) {
					uni.showToast({
						title: '无法开始录音',
						icon: 'none'
					});
				}
			},

			playAudio(src) {
				if (!src) return;
				if (this.recording) {
					try {
						this.recorderManager.stop();
					} catch (e) {}
				}
				if (this.playingSrc === src) {
					try {
						this.innerAudioContext.seek ? this.innerAudioContext.seek(0) : this.innerAudioContext.stop();
					} catch (e) {
						this.innerAudioContext.stop();
					}
					this.innerAudioContext.play();
					return;
				}
				this.playingSrc = src;
				this.innerAudioContext.stop();
				this.innerAudioContext.src = src;
				setTimeout(() => this.innerAudioContext.play(), 50);
			},

			formatDuration(ms) {
				const s = Math.max(0, Math.round(ms / 1000));
				const m = Math.floor(s / 60);
				const r = s % 60;
				return `${m}:${r < 10 ? '0' + r : r}`;
			},
			submitScore() {
				const score = this.totalScored; //audioDescs
				//console.log(this.pictureBooks)
				const page = JSON.stringify(this.pictureBooks); // ✅ 转为 JSON 字符串
				const maxScore = this.totalMaxScore;
				const classId = this.classId; //reviewPage
				const studentId = this.studentId; //studentId
				if (!score || score === 0) {
					uni.showToast({
						title: '暂无可提交的得分',
						icon: 'none'
					});
					return;
				}

				// 确认提示
				uni.showModal({
					title: '提交积分',
					content: `确认提交当前得分 ${score}/${maxScore} 吗？`,
					confirmText: '提交',
					cancelText: '取消',
					success: async (res) => {
						if (!res.confirm) return;
						api.pointSubmit({
							audioUrls: page,
							audioDescs: score,
							reviewPage: classId,
							studentId: studentId
						}).then(res1 => {
							if (res1 && res1.code == 200) {
								uni.showToast({
									title: '提交成功',
									icon: 'success'
								});
							}
						});

					}
				});
			},
			listByStudentAndClass() {
				const classId = this.classId; // reviewPage
				const studentId = this.studentId; // studentId

				api.listByStudentAndClass({
					reviewPage: classId,
					studentId: studentId
				}).then(res1 => {
					if (res1 && res1.code === 200 && Array.isArray(res1.data) && res1.data.length > 0) {

						let record = res1.data[0];
						let parsed = [];

						try {
							// ✅ audioUrls 可能是 JSON 字符串，也可能是对象或空
							if (record.audioUrls && record.audioUrls !== 'null' && record.audioUrls !== '') {
								parsed = typeof record.audioUrls === 'string' ?
									JSON.parse(record.audioUrls) :
									record.audioUrls;
							}
						} catch (e) {
							console.error('audioUrls 解析失败:', e);
							parsed = [];
						}

						// ✅ 仅当 parsed 有有效内容时才覆盖 pictureBooks
						if (Array.isArray(parsed) && parsed.length > 0) {
							this.pictureBooks = parsed;
						} else {
							console.log('audioUrls 为空或无效，保持现有 pictureBooks 不变');
						}
					}
				}).catch(err => {
					console.error('listByStudentAndClass 调用出错:', err);
				}).finally(() => {
					this.$nextTick(() => {
						console.log("resize is done!")
						this.$refs.collapse.resize()
					})
				});;
			}
		}
	};
</script>

<style scoped>
	.container {
		padding: 20rpx;
		background: linear-gradient(to bottom, #e6f4ea 0%, #fdfaf4 100%);
	}

	.section {
		margin-top: 10rpx;
	}

	.collapse-box>>>.uni-collapse-item {
		background: linear-gradient(to right, #f9fbf8, #fdfdfd);
		border-radius: 12rpx;
		margin-bottom: 20rpx;
		overflow: hidden;
		box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05);
		padding: 20rpx;
	}

	.book-title {
		display: flex;
		align-items: center;
		gap: 20rpx;
		margin: 30rpx 0rpx;
		font-size: 26rpx;
	}

	.book-name {
		color: #2b3a2e;
		font-weight: 600;
	}

	.ppt-card {
		margin-bottom: 20rpx;
		border: 2rpx solid #e0e0e0;
		border-radius: 10rpx;
		padding: 20rpx;
		background: #ffffff;
		box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05);
		text-align: center;
		/* min-height: 800rpx; */
		/* 🔹根据你录音+反馈区域高度调整 */
		position: relative;
	}


	.ppt-code {
		font-size: 24rpx;
		color: #444;
		margin-bottom: 12rpx;
		font-weight: bold;
	}

	.ppt-image {
		max-width: 100%;
		border-radius: 8rpx;
		margin-bottom: 12rpx;
		border: 2rpx solid #f0f0f0;
	}

	.text-row {
		margin-top: 10rpx;
		padding: 8rpx 14rpx;
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 14rpx;
	}

	.speaker {
		width: 48rpx;
		height: 48rpx;
		border-radius: 50%;
		background: #eef6ef;
		border: 2rpx solid #cfe6d2;
		user-select: none;
	}

	.speaker.playing {
		animation: pulse 1.2s ease-in-out infinite;
		border-color: #7bc27a;
	}

	@keyframes pulse {
		0% {
			box-shadow: 0 0 0 0 rgba(123, 194, 122, .35);
		}

		70% {
			box-shadow: 0 0 0 12rpx rgba(123, 194, 122, 0);
		}

		100% {
			box-shadow: 0 0 0 0 rgba(123, 194, 122, 0);
		}
	}

	.meaning-text {
		font-size: 32rpx;
		color: #222;
		line-height: 1.6;
	}

	/* 录音区域 */
	.record-row {
		margin-top: 12rpx;
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 16rpx;
	}

	.record-btn,
	.play-rec-btn {
		display: inline-flex;
		align-items: center;
		gap: 10rpx;
		padding: 8rpx 16rpx;
		border: 2rpx solid #d6e6d9;
		border-radius: 999rpx;
		background: #f4faf5;
	}

	.record-btn.recording {
		background: #ffecec;
		border-color: #ffb3b3;
	}

	.mic-icon,
	.play-icon {
		width: 28rpx;
		height: 28rpx;
	}

	.btn-text {
		font-size: 26rpx;
	}

	.dur {
		font-size: 24rpx;
		color: #888;
	}

	.play-rec-btn {
		margin-right: 4rpx;
	}

	.del-rec-btn {
		border: 2rpx solid #f2b1b1;
		background: #fff5f5;
		color: #e74c3c;
		border-radius: 999rpx;
		padding: 8rpx 16rpx;
	}

	.del-rec-btn:active {
		background: #ffe9e9;
		border-color: #ee9a9a;
	}

	.ai-feedback {
		margin-top: 22rpx;
		padding: 10rpx 16rpx;
		background: #f0f9f1;
		color: #2c5a2e;
		font-size: 26rpx;
		border-radius: 8rpx;
		line-height: 1.6;
		text-align: left;
	}

	.score-bar {
		margin-top: 16rpx;
		padding: 16rpx 20rpx;
		border-radius: 12rpx;
		background: linear-gradient(to right, #eef8f0, #f8fcf2);
		border: 2rpx solid #d9eadb;
		display: flex;
		align-items: baseline;
		gap: 12rpx;
		justify-content: center;
	}

	.score-text {
		font-size: 40rpx;
		font-weight: 800;
		color: #2b5a33;
	}

	.score-sub {
		font-size: 24rpx;
		color: #6a7a6e;
	}

	.submit-score-btn {
		position: fixed;
		right: 40rpx;
		bottom: 60rpx;
		background: linear-gradient(135deg, #4caf50, #66bb6a);
		color: #fff;
		font-weight: bold;
		font-size: 30rpx;
		padding: 18rpx 40rpx;
		border-radius: 999rpx;
		box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.15);
		z-index: 999;
	}

	.submit-score-btn:active {
		transform: scale(0.96);
		background: linear-gradient(135deg, #43a047, #5cb85c);
	}
</style>